package be.sc.classicnetwork.model;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.Lob;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.math.NumberUtils;
import org.hibernate.annotations.Cascade;
import org.hibernate.annotations.Filter;
import org.hibernate.annotations.FilterDef;
import org.hibernate.annotations.Index;
import org.hibernate.validator.NotEmpty;
import org.hibernate.validator.NotNull;

import be.sc.classicnetwork.controller.validator.Expression;
import be.sc.classicnetwork.util.Assert;
import be.sc.classicnetwork.util.Utils;

/**
 * Entity for Car.
 */
@Entity
@FilterDef(name = "published", defaultCondition = "1 = published")
@Filter(name = "published")
@Expression(assertTrue = "this.priceFilled", message = "error.price")
public class Car extends AbstractEntity {

	public static final String PUBLISHED_FILTER = "published";

	private static final long serialVersionUID = -1379108734919310798L;

	@ManyToOne
	private Image thumb;

	@ManyToOne
	@NotNull
	private Make make;

	@ManyToOne
	private Type type;

	@NotEmpty
	@Index(name = "modelIndex")
	private String model;

	private Integer salesPrice;

	private String alternativeTextPrice;

	private Integer agreedPrice;

	@NotEmpty
	private String year;

	private Integer kms;

	private Integer cc;

	private String chassisNumber;

	private String engineNumber;

	private String interior;

	private String colorExterior;

	@NotEmpty
	private String drive;

	private boolean published;

	private Integer cv;

	@ManyToOne
	private Site site;

	@ManyToOne
	private Location location;

	@Lob
	private String description;

	@Lob
	private String comment;

	@ManyToOne
	private Contact contact;

	@OneToMany(cascade = { CascadeType.ALL })
	@Cascade(org.hibernate.annotations.CascadeType.DELETE_ORPHAN)
	private List<Image> images = new ArrayList<Image>();

	@ManyToMany
	private Set<Option> options = new HashSet<Option>();

	/**
	 * Constructor.
	 */
	public Car() {
		super();
	}

	/**
	 * Constructor.
	 */
	public Car(String id) {
		super(id);
	}

	/**
	 * addImage.
	 */
	public void addImage(Image image) {
		this.images.add(image);
	}

	/**
	 * Removes the specified images.
	 */
	public void removeImages(Image... images) {
		Assert.notNull(images);
		for (Image image : images) {
			// don't remove the thumb image
			if (image.equals(this.thumb)) {
				continue;
			}
			this.images.remove(image);
		}
	}

	/**
	 * Removes the this images that are empty.
	 */
	public void removeEmptyImages() {
		Assert.notNull(this.images);
		for (Iterator<Image> iterator = this.images.iterator(); iterator
				.hasNext();) {
			if (iterator.next().isEmpty()) {
				iterator.remove();
			}
		}
	}

	/**
	 * Adds a default image if none image has been provided yet.
	 */
	public void addDefaultImageIfNone() {
		Assert.notNull(this.images);
		if (this.images.size() == 0) {
			addImage(new Image(Utils.getDefaultImage()));
		}
	}

	/**
	 * Sets the thumb with the first image if it has not yet been set. Assert
	 * that the car has at least one image.
	 */
	public void setThumbIfNone() {
		Assert.notEmpty(this.images);
		if (this.thumb == null) {
			this.thumb = this.images.get(0);
		}
	}

	/**
	 * getImagesAsByteArray.
	 */
	public Collection<byte[]> getImagesAsByteArray() {
		ArrayList<byte[]> imgs = new ArrayList<byte[]>();
		for (Image image : this.images) {
			imgs.add(image.getFile());
		}
		return imgs;
	}

	/**
	 * addOption.
	 */
	public void addOption(Option option) {
		options.add(option);
	}

	/**
	 * Replaces carriage return with <br>
	 * (for HTML).
	 */
	public String getDescriptionHtml() {
		return (description != null) ? getDescription().replaceAll("\\n",
				"<br>") : null;
	}

	/**
	 * Checks one of sales price or alternativeTextPrice is not empty.
	 */
	public boolean isPriceFilled() {
		return (salesPrice != null || !StringUtils
				.isEmpty(alternativeTextPrice));
	}

	public List<Image> getImages() {
		return images;
	}

	public void setImages(List<Image> images) {
		this.images = images;
	}

	public Integer getCc() {
		return cc;
	}

	public void setCc(Integer cc) {
		this.cc = cc;
	}

	public String getChassisNumber() {
		return chassisNumber;
	}

	public void setChassisNumber(String chassisNumber) {
		this.chassisNumber = chassisNumber;
	}

	public String getDescription() {
		return description;
	}

	public void setDescription(String description) {
		this.description = description;
	}

	public String getEngineNumber() {
		return engineNumber;
	}

	public void setEngineNumber(String engineNumber) {
		this.engineNumber = engineNumber;
	}

	public String getColorExterior() {
		return colorExterior;
	}

	public void setColorExterior(String externalColor) {
		this.colorExterior = externalColor;
	}

	public String getInterior() {
		return interior;
	}

	public void setInterior(String interiorColor) {
		this.interior = interiorColor;
	}

	public Make getMake() {
		return make;
	}

	public void setMake(Make make) {
		this.make = make;
	}

	public String getModel() {
		return model;
	}

	public void setModel(String model) {
		this.model = model;
	}

	public Integer getSalesPrice() {
		return salesPrice;
	}

	public void setSalesPrice(Integer price) {
		this.salesPrice = price;
	}

	public String getDrive() {
		return drive;
	}

	public void setDrive(String transmission) {
		this.drive = transmission;
	}

	public Integer getKms() {
		return kms;
	}

	public void setKms(Integer kms) {
		this.kms = kms;
	}

	public String getYear() {
		return year;
	}

	public void setYear(String year) {
		this.year = year;
	}

	public boolean isPublished() {
		return published;
	}

	public void setPublished(boolean published) {
		this.published = published;
	}

	public Site getSite() {
		return site;
	}

	public void setSite(Site site) {
		this.site = site;
	}

	public Image getThumb() {
		return thumb;
	}

	public void setThumb(Image thumb) {
		this.thumb = thumb;
	}

	public Set<Option> getOptions() {
		return options;
	}

	public void setOptions(Set<Option> options) {
		this.options = options;
	}

	public Integer getCv() {
		return cv;
	}

	public void setCv(Integer cv) {
		this.cv = cv;
	}

	public Type getType() {
		return type;
	}

	public void setType(Type type) {
		this.type = type;
	}

	public Contact getContact() {
		return contact;
	}

	public void setContact(Contact contact) {
		this.contact = contact;
	}

	public Location getLocation() {
		return location;
	}

	public void setLocation(Location location) {
		this.location = location;
	}

	public Integer getAgreedPrice() {
		return agreedPrice;
	}

	public void setAgreedPrice(Integer agreedPrice) {
		this.agreedPrice = agreedPrice;
	}

	public String getComment() {
		return comment;
	}

	public void setComment(String myDescription) {
		this.comment = myDescription;
	}

	public String getAlternativeTextPrice() {
		return alternativeTextPrice;
	}

	public void setAlternativeTextPrice(String alternativeTextPrice) {
		// set the price as number or alternative text
		if (NumberUtils.isNumber(alternativeTextPrice)) {
			this.alternativeTextPrice = null;
			this.salesPrice = NumberUtils.createInteger(alternativeTextPrice);
		} else {
			this.salesPrice = null;
			this.alternativeTextPrice = alternativeTextPrice;
		}
	}

}
